/*
 * @(#)StateTraceControllerImpl.java   09/10/11
 * 
 * Copyright (C) 2009 Ecole des Mines d'Ales
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 * Contact: yulinz88@gmail.com
 */



package fr.ema.arch3d.ref.interceptors.impls;

import fr.ema.arch3d.ref.interceptors.interfaces.StateTraceController;

import org.objectweb.fractal.api.NoSuchInterfaceException;
import org.objectweb.fractal.julia.loader.Initializable;
import org.objectweb.fractal.julia.loader.Tree;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;

import java.util.Vector;

//~--- classes ----------------------------------------------------------------

/**
 * Class description
 *
 *
 * @version        1.0, 09/09/13
 * @author         Huaxi (Yulin) ZHANG
 */
public class StateTraceControllerImpl implements StateTraceController, Initializable {

    /** Field description */
    private boolean recordPut = false;

    /** Field description */
    private boolean begin  = false,
                    change = false;

    /** Field description */
    private boolean returnState = false;

    /** Field description */
    private Vector activeTime;

    /** Field description */
    private String changeFieldsString;

    /** Field description */
    private Field[] fields;

    /** Field description */
    private int first;

    /** Field description */
    private Vector firstState;

    /** Field description */
    private long start;

    /** Field description */
    private Vector state;

    /** Field description */
    private Vector stateTrace;

    /**
     * Method description
     *
     * @param args  
     */
    public void initialize(final Tree args) {
        String s = args.getSubTree(0).toString();
    }

    /**
     * Method description
     *
     * @param itf  
     * @param target  
     * @param method  
     * @throws IllegalAccessException  
     * @throws IllegalArgumentException  
     * @throws InstantiationException  
     * @throws InvocationTargetException  
     * @throws NoSuchFieldException  
     * @throws NoSuchInterfaceException  
     * @throws SecurityException  
     */
    public void preMethod(final String itf, final Object target, final String method)
            throws NoSuchInterfaceException, SecurityException, NoSuchFieldException, IllegalAccessException,
                   InstantiationException, IllegalArgumentException, InvocationTargetException {
        if (returnState) {
            for (int i = 0; i < this.fields.length; i++) {
                this.fields[i].set(target, this.state.get(i));

                // System.out.println(fields[i].getName());
            }

            this.returnState = false;
        }

        if (begin) {

            // System.out.println("here");
            this.start              = System.currentTimeMillis();
            this.change             = false;
            this.changeFieldsString = "";

            if (this.first == 0) {
                this.activeTime.add(new Long(this.start));
                this.fields = target.getClass().getDeclaredFields();

                for (int i = 0; i < fields.length; i++) {
                    this.firstState.add(fields[i].get(target));
                }
            }

            for (int i = 0; i < this.fields.length; i++) {

                // System.out.println(fields[i].getName());
            }

            this.first++;
        }
    }

    /**
     * Method description
     *
     * @param itf  
     * @param target  
     * @param method  
     * @throws IllegalAccessException  
     * @throws IllegalArgumentException  
     * @throws NoSuchInterfaceException  
     */
    public void postMethod(final String itf, final Object target, final String method)
            throws NoSuchInterfaceException, IllegalArgumentException, IllegalAccessException {

        // if begin online test*
        if (begin) {

            // if the fields have been changed
            if (this.change) {

                // record changed fields name
                String[] changedFieldsName = this.changeFieldsString.split("/");

                // value
                Object[] changedFieldsValue = new Object[changedFieldsName.length];

                for (int i = 0; i < changedFieldsName.length; i++) {
                    for (int j = 0; j < this.fields.length; j++) {
                        if (changedFieldsName[i].equals(this.fields[j].getName())) {
                            changedFieldsValue[i] = this.fields[j].get(target);
                        }
                    }
                }

                for (int i = 0; i < changedFieldsName.length; i++) {

                    // System.out.println("    "+changedFieldsName[i]+"="+changedFieldsValue[i]);
                    // record change time
                    this.activeTime.add(new Long(this.start));
                }

                // add this to stateTrace
                this.stateTrace.add(changedFieldsName);
                this.stateTrace.add(changedFieldsValue);
            }

            // System.out.println("state"+stateTrace);
        }
    }

    /**
     * Method description
     *
     */
    public void beginOnlineTest() {
        this.stateTrace = new Vector();

        // this.fieldPrivate = new Vector();
        this.firstState = new Vector();
        this.first      = 0;
        this.activeTime = new Vector();
        this.recordPut  = true;
        this.begin      = true;
    }

    /**
     * Method description
     *
     * @param name  
     * @param owner  
     */
    public void putField(String name, String owner) {
        if (this.recordPut) {
            this.change             = true;
            this.changeFieldsString = this.changeFieldsString.concat(name + "/");
        }
    }

    /**
     * Method description
     *
     * @param time  
     */
    public void returnState(long time) {
        int returnIndex;

        state = new Vector();
        state = this.firstState;

        for (int i = 1; i < this.activeTime.size() - 1; i++) {
            String t   = this.activeTime.get(i).toString();
            long   tt  = Long.parseLong(t);
            String t1  = this.activeTime.get(i + 1).toString();
            long   tt1 = Long.parseLong(t1);

            if ((tt < time) && (tt1 > time)) {
                System.err.println("i" + i + this.activeTime.size());
                returnIndex = i;

                for (int j = 0; j < returnIndex; j++) {
                    String[] changedName  = (String[]) this.stateTrace.get(j * 2);
                    Object[] changedValue = (Object[]) this.stateTrace.get(j * 2 + 1);

                    for (int k = 0; k < changedName.length; k++) {
                        for (int l = 0; l < this.fields.length; l++) {
                            if (changedName[k].equals(this.fields[l].getName())) {
                                state.set(l, changedValue[k]);
                            }
                        }
                    }
                }
            }
        }

        for (int i = 0; i < this.fields.length; i++) {
            this.returnState = true;
        }
    }
}
